package fishBids.group3.behaviour.auctioneer;

import es.upm.agents.ontology.ListProduct;
import es.upm.agents.ontology.Product;
import es.upm.agents.ontology.ToBid;
import es.upm.agents.ontology.ToGetPrice;
import es.upm.agents.ontology.ToGetPriceResponse;
import es.upm.agents.ontology.ToItemCancelled;
import es.upm.agents.ontology.ToNotifyWinner;
import es.upm.agents.ontology.ToVariatePrice;
import fishBids.group3.AgAuctioneer;
import fishBids.group3.common.Constants;
import fishBids.group3.utils.Utils;
import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.behaviours.SimpleBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

public class AuctioneerPriceVariatedBehaviour extends SimpleBehaviour {

	/**
	 * 
	 */
	private static final long serialVersionUID = 7373673590070014202L;
	
	public AuctioneerPriceVariatedBehaviour(AgAuctioneer auctioneerAg, Product product, AID market){
		this.auctioneerAg = auctioneerAg;
		this.product = product;
		this.market = market;		
	}

	
	@Override
	public void action() {

		if (this.auctioneerAg.isRegistrationOver()) {
			
			if(status == 1){//this means that a getPrice message has been received
				//System.out.println("After getting price and returning");
				status = 0;
				block(timeout);
				return;
			}
			
			if (!this.auctioneerAg.isAuctionStart()) {
				System.out.println("auctioneer3: the auction begins");
				this.auctioneerAg.setAuctionStart(true);
				this.auctioneerAg.setItemOver(true);
				time = System.currentTimeMillis();

			}
			// Start initiate the product

			if (this.auctioneerAg.isItemOver()) {
			   
				manageItemOver();

			}

			if (!buyProcess) {
				
				manageBuyProcess();


			}

			if (msg != null && winner == null) {

				boolean success = manageBuyAndNotify();
				while (!success){//error happens, process the next message in the queue if any
					success = true;//reset success
					if (!buyProcess) {					
						manageBuyProcess();
					}

					if (msg != null && winner == null) {
						success = manageBuyAndNotify();
					}
				}
			}

			// Send ItemCancelled message if the auction price is lower than
			// minimum price
			if ((!this.auctioneerAg.isItemOver())
					&& (auctionPrice < minimumPrice)) {
				manageItemCanceled();
			}
			// Check if itemOver is set
			if (this.auctioneerAg.isItemOver()) {
				//System.out.println("Auctioneer >>> stop Pricevariated");
				finished = true;
				return;
			}
			// Sending PriceVariated message
			System.out.println("auctioneer3: who offers " + auctionPrice
					+ " euros?");
			ToVariatePrice toVariatePrice = new ToVariatePrice();
			product.setPrice(auctionPrice);
			toVariatePrice.setProduct(product);

			Utils.sendMessageWithConceptAgentsCollection(ACLMessage.INFORM,
					this.auctioneerAg.getCodec(), this.auctioneerAg
							.getOntology(), this.auctioneerAg
							.getBiddersRegistered(),
					Constants.PRICE_VARIATED, this.auctioneerAg,
					toVariatePrice);
			auctionPrice = (float) (auctionPrice - StepReduce);

			return;
		}

		else {
			block(10000);
		}

	}
	
	@Override
	public boolean done() {
		return finished;
	}		
	
	private void manageItemCanceled() {
		ToItemCancelled toItemCancelled = new ToItemCancelled();
		product.setPrice((float) (auctionPrice + StepReduce));
		toItemCancelled.setProduct(product);
		Utils.sendMessageWithConceptAgentsCollection(ACLMessage.INFORM,
				this.auctioneerAg.getCodec(), this.auctioneerAg
						.getOntology(), this.auctioneerAg
						.getBiddersRegistered(),
				Constants.ITEM_CANCELLED, this.auctioneerAg,
				toItemCancelled);
		//System.out.println("auctioneer3>>> Item Cancelled");
		this.auctioneerAg.setItemOver(true);
		
	}

			
	private boolean manageBuyAndNotify() {

		// Checking if the item can be sold
		if (!buyProcess) {

			buyProcess = true;
			toNotifyWinner = new ToNotifyWinner();
			product.setPrice((float) (auctionPrice + StepReduce));
			toNotifyWinner.setProduct(product);
			toNotifyWinner.setWinner(msg.getSender());
			
			ToBid toBid = (ToBid) Utils.extractContent(this.auctioneerAg, msg);
			if(!isValidBid(toBid)){
				buyProcess = false;
				return false; //continue with the behaviour
			}

			Utils.sendGenericMessageWithObject(ACLMessage.REQUEST,
					this.auctioneerAg.getBankID(),
					Constants.BUY_PRODUCT, this.auctioneerAg,
					toNotifyWinner);
		}

		MessageTemplate mt = MessageTemplate.and(MessageTemplate
				.MatchProtocol(Constants.BUY_PRODUCT), MessageTemplate
				.and(MessageTemplate.MatchLanguage(this.auctioneerAg
						.getCodec().getName()), MessageTemplate
						.MatchOntology(this.auctioneerAg.getOntology()
								.getName())));

		ACLMessage buyProductMsg = this.auctioneerAg.blockingReceive(mt);
			if (buyProductMsg.getPerformative() == ACLMessage.INFORM) {

				winner = msg.getSender();
//				System.out.println("Auctioneer >>>> the product "
//						+ product.getProductType().getType()
//						+ " could be sold to bidder: "
//						+ winner.getName());
				// Send NOTIFYWINNER message

				Utils.sendMessageWithConceptAgentsCollection(
						ACLMessage.INFORM,
						this.auctioneerAg.getCodec(), this.auctioneerAg
								.getOntology(), this.auctioneerAg
								.getBiddersRegistered(),
						Constants.NOTIFY_WINNERS, this.auctioneerAg,
						toNotifyWinner);
				System.out.println("auctioneer3: sold "
						+ toNotifyWinner.getProduct().getAmount()
						+ " kg. of "
						+ toNotifyWinner.getProduct().getProductType()
								.getType() + " to bidder "
						+ toNotifyWinner.getWinner().getName() + " by "
						+ toNotifyWinner.getProduct().getPrice()
						+ " euros");
				this.auctioneerAg.setItemOver(true);
				return true;
			} else {
				buyProcess = false;
//				System.out.println("Auctioneer >>>> the product "
//						+ product.getProductType().getType()
//						+ " could NOT be sold to bidder: "
//						+ msg.getSender());
				status = 1;
				return false;
			}
//		}
	}


	private boolean isValidBid(ToBid toBid) {
		
		Product bidProduct = toBid.getProduct();
		if( product.getPrice() != bidProduct.getPrice()  ||
				!product.getProductType().getType().equals(bidProduct.getProductType().getType() ) || 
				product.getAmount() != bidProduct.getAmount() ){
			//System.out.println("Bid for wrong product, can not process");
			return false;
		}
		return true;
	}


	private void manageBuyProcess() {


		// Receive BID message
		MessageTemplate mt1 = MessageTemplate
				.and(
						MessageTemplate.MatchLanguage(this.auctioneerAg
								.getCodec().getName()),
						MessageTemplate
								.and(
										MessageTemplate
												.MatchOntology(this.auctioneerAg
														.getOntology()
														.getName()),
										MessageTemplate
												.and(
														MessageTemplate
																.MatchProtocol(Constants.BID),
														MessageTemplate
																.MatchPerformative(ACLMessage.INFORM))));

		msg = this.auctioneerAg.receive(mt1);
		block(timeout);		
		
	}


	private void manageItemOver() {
		
		ToGetPrice toGetPrice = new ToGetPrice();
		toGetPrice.setProductType(product.getProductType());
		marketPrice = getPriceBehaviour(toGetPrice, market);
		
		//System.out.println("Auctioneer>>> marketPrice: " + marketPrice);
		auctionPrice = (float) (marketPrice * (1 + (Math.random() / 4)))
				* product.getAmount(); // initial price set by
		// Auctioneer
		product.setPrice(auctionPrice);
		System.out.println("auctioneer3: the auction of "
				+ product.getAmount() + " kg. of "
				+ product.getProductType().getType());
		minimumPrice = marketPrice * (float) Math.random() / 4
				* product.getAmount(); // Set the minimum price of the
		// item before auctioning
		//System.out.println("Minimum auction price: " + minimumPrice);
		this.auctioneerAg.setItemOver(false);
	}

		
	public float getPriceBehaviour(ToGetPrice toGetPrice, AID market){
		float marketprice = 0;
		if(status == 0){
	
			Utils.sendMessageWithConcept(ACLMessage.QUERY_REF, this.auctioneerAg.getCodec(), this.auctioneerAg.getOntology(), market, Constants.GET_PRICE, this.auctioneerAg, toGetPrice);
	
			// Waits for the arrival of an answer
			//System.out.println("Auctioneer>> Sent GetPrice msg. Waiting for an INFORM or REFUSE message");
			status = 1;
		}
		// Receive the message
		ACLMessage msg = this.auctioneerAg.blockingReceive(MessageTemplate.MatchProtocol(Constants.GET_PRICE)); 
		
		if(msg != null ){

			try {
				ContentElement ce = null;
				if (msg.getPerformative() == ACLMessage.INFORM) {
					//System.out.println("Auctionneer>>> received INFORM message arrived from Market");
					ce = this.auctioneerAg.getContentManager().extractContent(msg);
					if (ce instanceof Action) {
						Action ac = (Action) ce;
						Concept cpt = ac.getAction();
						if (cpt instanceof ToGetPriceResponse) {
							ToGetPriceResponse toGetPriceResponse = (ToGetPriceResponse) cpt;
							marketprice = toGetPriceResponse.getPrice();
//							System.out.println(this.auctioneerAg.getName()
//									+ " receive GetPrice of type: "
//									+ toGetPriceResponse.getProductType().getType() + " with price: "
//									+ toGetPriceResponse.getPrice());
						}
					}
					return marketprice;
				}
				if (msg.getPerformative() == ACLMessage.NOT_UNDERSTOOD) {
//					System.out.println(this.auctioneerAg.getName()
//							+ " got a NOT_UNDERSTOOD in getPrice, check!!!!! :(");
					return 0;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		
		}
			return 0;


	}
	
	AgAuctioneer auctioneerAg;
	ListProduct listProduct;
	boolean buyProcess = false;
	boolean finished = false;
    
	Product product = new Product();
	float marketPrice;
	float auctionPrice; 
	float StepReduce = Float.parseFloat(Utils.getProperty(Constants.AMOUNT_DECREASED));
	int timeout = Integer.parseInt(Utils.getProperty(Constants.DIMINISH_TIME));
	int status = 0;
	long time = 0;
	AID winner = null;
	AID market;
	float minimumPrice;
	ACLMessage msg = null;	
	ToNotifyWinner toNotifyWinner = null;
	ToGetPrice toGetPrice = null;

}
